package consistent_hash

import (
	"hash/crc32"
	"sort"
	"strconv"
)

// Hash function maps bytes to uint32
type Hash func(data []byte) uint32

type Map struct {
	hash     Hash
	replicas int
	keys     []int // sorted
	hashMap  map[int]string
}

func New(replicas int, fn Hash) *Map {
	m := &Map{
		hash:     fn,
		replicas: replicas,
		hashMap:  make(map[int]string),
	}

	if m.hash == nil {
		m.hash = crc32.ChecksumIEEE
	}

	return m
}

func (m *Map) Add(keys ...string) {
	for _, key := range keys {
		for i := 0; i < m.replicas; i++ {
			hashNum := m.hash([]byte(key + strconv.Itoa(i)))
			m.hashMap[int(hashNum)] = key
			m.keys = append(m.keys, int(hashNum))
		}
	}

	sort.Ints(m.keys)
}

func (m *Map) Get(key string) string {
	count := len(m.keys)
	if count == 0 {
		return ""
	}

	if count == 1 {
		return m.hashMap[m.keys[0]]
	}

	hashNum := m.hash([]byte(key))
	for i := 0; i < count-1; i++ {
		if int(hashNum) >= m.keys[i] && int(hashNum) < m.keys[i+1] {
			return m.hashMap[m.keys[i]]
		}
	}

	return m.hashMap[m.keys[0]]
}
